﻿using System;
using System.Linq;
using MyFramework.Common.Interfaces;

namespace MyFramework.Core
{
    /// <summary>
    /// controller metadata
    /// </summary>
    public class ControllerMetadata
    {
        #region Fields

        /// <summary>
        /// The controller
        /// </summary>
        private ControllerBase controller;

        /// <summary>
        /// The startup action name
        /// </summary>
        public const string StartupActionName = "__StartupAction__";

        /// <summary>
        /// The unload view action name
        /// </summary>
        public const string UnloadViewActionName = "__UnloadViewAction__";

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="ControllerMetadata"/> class.
        /// </summary>
        /// <param name="controller">The controller.</param>
        internal ControllerMetadata(ControllerBase controller)
        {
            this.controller = controller;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Converts the parameter.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parameters">The parameters.</param>
        /// <param name="position">The position.</param>
        /// <returns></returns>
        internal static T ConvertParameter<T>(object[] parameters, int position)
        {
            try
            {
                object p = parameters.ElementAtOrDefault(position);

                if (p == null)
                {
                    return default(T);
                }
                else
                {
                    return (T)p;
                }
            }
            catch (InvalidCastException)
            {
                return default(T);
            }
        }

        /// <summary>
        /// Maps the startup action automatic method.
        /// </summary>
        /// <param name="execute">The execute.</param>
        public void MapStartupActionToMethod(Action execute)
        {
            this.MapActionToMethodImpl(StartupActionName, p => execute(), p => true);
        }

        /// <summary>
        /// Maps the unload view action automatic method.
        /// </summary>
        /// <param name="execute">The execute.</param>
        public void MapUnloadViewActionToMethod(Action execute)
        {
            this.MapActionToMethodImpl(UnloadViewActionName, p => execute(), p => true);
        }

        /// <summary>
        /// Maps the unload view action automatic method.
        /// </summary>
        /// <param name="execute">The execute.</param>
        public void MapUnloadViewActionToMethod(Action<IView> execute)
        {
            this.MapActionToMethodImpl(UnloadViewActionName, p => execute(ConvertParameter<IView>(p, 0)), p => true);
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        public void MapActionToMethod(string actionName, Action execute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(), p => true);
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public void MapActionToMethod(string actionName, Action execute, Func<bool> canExecute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(), p => canExecute());
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        public void MapActionToMethod<T>(string actionName, Action<T> execute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T>(p, 0)), p => true);
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public void MapActionToMethod<T>(string actionName, Action<T> execute, Func<T, bool> canExecute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T>(p, 0)), p => canExecute(ConvertParameter<T>(p, 0)));
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T0">The type of the 0.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        public void MapActionToMethod<T0, T1>(string actionName, Action<T0, T1> execute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1)), p => true);
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T0">The type of the 0.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public void MapActionToMethod<T0, T1>(string actionName, Action<T0, T1> execute, Func<T0, T1, bool> canExecute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1)), p => canExecute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1)));
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T0">The type of the 0.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        public void MapActionToMethod<T0, T1, T2>(string actionName, Action<T0, T1, T2> execute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1), ConvertParameter<T2>(p, 2)), p => true);
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T0">The type of the 0.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public void MapActionToMethod<T0, T1, T2>(string actionName, Action<T0, T1, T2> execute, Func<T0, T1, T2, bool> canExecute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1), ConvertParameter<T2>(p, 2)), p => canExecute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1), ConvertParameter<T2>(p, 2)));
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T0">The type of the 0.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <typeparam name="T3">The type of the 3.</typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        public void MapActionToMethod<T0, T1, T2, T3>(string actionName, Action<T0, T1, T2, T3> execute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1), ConvertParameter<T2>(p, 2), ConvertParameter<T3>(p, 3)), p => true);
        }

        /// <summary>
        /// Maps the action automatic method.
        /// </summary>
        /// <typeparam name="T0">The type of the 0.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <typeparam name="T3">The type of the 3.</typeparam>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public void MapActionToMethod<T0, T1, T2, T3>(string actionName, Action<T0, T1, T2, T3> execute, Func<T0, T1, T2, T3, bool> canExecute)
        {
            this.MapActionToMethodImpl(actionName, p => execute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1), ConvertParameter<T2>(p, 2), ConvertParameter<T3>(p, 3)), p => canExecute(ConvertParameter<T0>(p, 0), ConvertParameter<T1>(p, 1), ConvertParameter<T2>(p, 2), ConvertParameter<T3>(p, 3)));
        }

        /// <summary>
        /// Maps the action automatic method implementation.
        /// </summary>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="doAction">The document action.</param>
        /// <param name="canDoAction">The can document action.</param>
        /// <exception cref="System.InvalidOperationException"></exception>
        private void MapActionToMethodImpl(string actionName, Action<object[]> doAction, Func<object[], bool> canDoAction)
        {
            if (this.controller.MappedActions.Any(t => t.Item1 == actionName))
            {
                throw new InvalidOperationException(string.Format(Resources.ActionAlreadyRegistered, actionName));
            }
            this.controller.MappedActions.Add(Tuple.Create(actionName, doAction, canDoAction));
        }

        #endregion
    }
}
